package com.file.parser;

import com.file.bo.ResponseData;
import com.file.bo.mail.CCB;
import com.file.bo.mail.CCBTran;
import com.file.constant.ErrorCode;
import com.file.parser.traprange.PDFTableExtractor;
import com.file.util.JsonUtils;
import com.google.common.base.Splitter;
import com.google.common.base.Strings;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.pdfbox.pdmodel.PDDocument;
import technology.tabula.*;
import technology.tabula.detectors.NurminenDetectionAlgorithm;
import technology.tabula.extractors.SpreadsheetExtractionAlgorithm;

import java.io.File;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Slf4j
public class CCBPdfParser extends BasePdfParser {

    private static final Integer CCB_PDF_HEADER_LINE_NUMBER = 3;
    private static final Integer CCB_PDF_FOOTER_LINE_NUMBER = 1;

    public ResponseData<String> parseCCBPdfToJson(String daId, String filePath) {
        log.info("parseCCBPdfToJson started, daId:{}", daId);
        String json = null;

        try {
            CCB ccb = parseCCBPdf(filePath);
            json = JsonUtils.convertObjectToJson(ccb);
        } catch (Exception e) {
            log.error("[OnError]parseCCBPdfToJson failed, daId:{}", daId, e);
            return new ResponseData<String>(json, ErrorCode.FILE_PARSE_EXCEPTION.getCode(),
                    ErrorCode.FILE_PARSE_EXCEPTION.getMsg());
        }

        log.info("parseCCBPdfToJson completed, daId:{}, filePath:{}", daId, filePath);
        return new ResponseData<String>(json, ErrorCode.SUCCESS.getCode(), ErrorCode.SUCCESS.getMsg());
    }

    private CCB parseCCBPdf(String filePath) {
        String pdfHeaderText = parsePdfHeaderText(filePath);
        CCB ccb = new CCB();
        List<CCBTran> ccbTrans = new ArrayList<>();
        if (pdfHeaderText.indexOf("结束日期：") > 0) {  //NOSONAR
            ccb = parseCCBHeader1(filePath);
            ccbTrans = parseCcbTrans1(filePath);
        } else if (pdfHeaderText.indexOf("起止日期:") > 0) {  //NOSONAR
            ccb = parseCCBHeader2(filePath);
            ccbTrans = parseCcbTrans2(filePath);
        }

        try {
            ccbTrans = parseCcbLastTwoColumns(filePath, ccbTrans);
        } catch (Exception e) {
            log.warn("parseCcbLastTwoColumns failed", e);
        }
        ccb.setCcbTrans(ccbTrans);

        return ccb;
    }

    private CCB parseCCBHeader1(String filePath) {
        CCB ccb = new CCB();
        String pdfHeaderText = parsePdfHeaderText(filePath);

        String accountNo = pdfHeaderText.substring(pdfHeaderText.indexOf("卡号/账号") + 6, pdfHeaderText.indexOf("客户名称")).trim();
        String name = pdfHeaderText.substring(pdfHeaderText.indexOf("客户名称：") + 5, pdfHeaderText.indexOf("起始日期：")).trim();
        String startDate = pdfHeaderText.substring(pdfHeaderText.indexOf("起始日期：") + 5, pdfHeaderText.indexOf("结束日期：")).trim();
        //250729:适配表头新增字段
        String endDate="";
        if(pdfHeaderText.contains("当前时间段收支金额合计")){
            endDate = pdfHeaderText.substring(pdfHeaderText.indexOf("结束日期：") + 5, pdfHeaderText.indexOf("当前时间段收支金额合计")).trim();
        }else{
            endDate = pdfHeaderText.substring(pdfHeaderText.indexOf("结束日期：") + 5, pdfHeaderText.indexOf("序号 摘要")).trim();
        }

        ccb.setAccountNo(accountNo);
        ccb.setName(name);
        ccb.setStartDate(startDate);
        ccb.setEndDate(endDate);

        return ccb;
    }

    private CCB parseCCBHeader2(String filePath) {
        CCB ccb = new CCB();
        String pdfHeaderText = parsePdfHeaderText(filePath);

        String accountNo = pdfHeaderText.substring(pdfHeaderText.indexOf("卡号/账号") + 6, pdfHeaderText.indexOf("客户名称")).trim();
        String name = pdfHeaderText.substring(pdfHeaderText.indexOf("客户名称") + 5, pdfHeaderText.indexOf("币别")).trim();
        String transDetailPeriod = pdfHeaderText.substring(pdfHeaderText.indexOf("起止日期") + 5, pdfHeaderText.indexOf("序号 摘要")).trim();
        String[] date = transDetailPeriod.split("-");
        String startDate = date[0];
        ccb.setStartDate(startDate);
        if (date.length > 1) {
            ccb.setEndDate(date[1]);
        }
        ccb.setAccountNo(accountNo);
        ccb.setName(name);

        return ccb;
    }

    private List<CCBTran> parseCcbTrans1(String filePath) {
        List<CCBTran> ccbTrans = new ArrayList<>();

        String transText = getPdfTextByStripper(filePath);
        if (Strings.isNullOrEmpty(transText)) {
            return ccbTrans;
        }

        List<List<String>> tranFieldsList = parseTransTextToTranFieldsList2(transText);
        // id
        String id = "";
        // 摘要
        String transactionType = "";
        // 币别
        String currency = "";
        // 钞汇
        String cashRemit = "";
        // 交易日期
        String transactionDate = "";
        // 交易金额
        String transactionAmount = "";
        // 账户余额
        String balance = "";
        // 交易地点/附言
        StringBuilder transactionComment = new StringBuilder();
        // 对方账号与户名
        String counterParty = "";


        // 每行的记录是否读取完成
        Boolean rowResult = false;
        for (int i = 0; i < tranFieldsList.size(); i++) {
            List<String> strings = tranFieldsList.get(i);
            // 下一行的数据
            List<String> nextStrings = new ArrayList<>();
            if (i < tranFieldsList.size() - 1) {
                nextStrings = tranFieldsList.get(i + 1);
            }
            if (StringUtils.isBlank(strings.get(0)) || strings.get(0).contains("中国建设银行个人活期账户全部交易明细") || strings.get(0).contains("卡号/账号:") || strings.get(0).contains("序号") || strings.get(0).contains("生成时间")) {
                continue;
            }
            String[] contents = strings.get(0).split(" ");
            if (contents[0].matches("\\d+") && contents.length >= 7) {
                id = contents[0];
                int tranDateIndex = 0;
                //  适配摘要有空格错位case
                //  24 FN202309190033648 T- 人民币元 钞 20230919 2,737.58
                //  20 JDZF + 退休假伙食费 + 人民币元 钞 20240417 645.30 649.87
                if (!contents[4].matches("\\d{8}")) {
                    for (int j = 0; j < contents.length; j++) {
                        if (contents[j].matches("\\d{8}")) {
                            tranDateIndex = j;
                            break;
                        }
                    }
                    // 根据tranDateIndex 定位摘要的endIndex,合并摘要移动摘要右边元素
                    if (tranDateIndex > 3) {
                        int startIndex = 1;
                        int endIndex = tranDateIndex - 3;
                        contents = concatenateTransactionType(contents, startIndex, endIndex);

                    }
                }

                transactionType = contents[1];
                currency = contents[2];
                cashRemit = contents[3];
                transactionDate = contents[4];
                transactionAmount = contents[5];
                if (contents.length > 6) {
                    balance = contents[6];
                }
                if (contents.length > 7) {
                    transactionComment = new StringBuilder(contents[7]);
                }
                // 交易地点附言 支付宝-支付宝-消费-App Store _
                if (contents.length > 8) {
                    if (contents[8].matches("\\d+\\S+") || StringUtils.equals(contents[7], "***")) {
                        counterParty = contents[8];
                    } else {
                        transactionComment.append(contents[8]);
                    }
                }
                if (contents.length > 9) {
                    if (contents[9].matches("\\d+\\S+")) {
                        counterParty = contents[9];
                    } else {
                        transactionComment.append(contents[9]);
                    }
                }
            } else {
                if (contents.length > 1) {
                    transactionComment.append(contents[0]);
                    counterParty += contents[1];

                } else {
                    //    适配附言和对方账号同时换行case
                    //    242 转账支取 人民币元 钞 20241023 -280,000.00 27,006.46 中国****中心
                    //    65*****69/乌***公
                    //    司
                    if (StringUtils.isBlank(counterParty) && !contents[0].matches("\\d+.*")) {
                        transactionComment.append(contents[0]);
                    } else {
                        counterParty += contents[0];
                    }

                }
            }
            //这一行完成解析
            if (nextStrings.get(0).split(" ")[0].matches("\\d+") || nextStrings.get(0).split(" ")[0].startsWith("生成时间")) {
                rowResult = true;
            }

            if (rowResult && id.matches("\\d+")) {
                CCBTran ccbTran = new CCBTran();
                ccbTran.setId(id);
                ccbTran.setTransactionType(transactionType);
                ccbTran.setCurrency(currency);
                ccbTran.setCashRemit(cashRemit);
                ccbTran.setTransactionDate(transactionDate);
                ccbTran.setTransactionAmount(transactionAmount);
                ccbTran.setBalance(balance);
                ccbTran.setTransactionComment(transactionComment.toString());
                ccbTran.setCounterParty(counterParty);
                ccbTrans.add(ccbTran);

                id = "";
                transactionType = "";
                currency = "";
                cashRemit = "";
                transactionDate = "";
                transactionAmount = "";
                balance = "";
                transactionComment = new StringBuilder();
                counterParty = "";
                rowResult = false;
            }

        }

        return ccbTrans;
    }

    private List<CCBTran> parseCcbTrans2(String filePath) {
        List<CCBTran> ccbTrans = new ArrayList<>();

        String pdfHeaderText = parsePdfHeaderText(filePath);
        String currency = pdfHeaderText.substring(pdfHeaderText.indexOf("币别") + 3, pdfHeaderText.indexOf("钞汇")).trim();
        String cashRemit = pdfHeaderText.substring(pdfHeaderText.indexOf("钞汇") + 3, pdfHeaderText.indexOf("起止日期")).trim();

        // 1. 读取文件
        File pdf = new File(filePath);

        // 2. pdfbox读取PDDocument
        try (PDDocument pdfDocument = PDDocument.load(pdf)) {

            // 3. tabula新建ObjectExtractor和NurminenDetectionAlgorithm，同时准备接收表格Rectangle的结构
            ObjectExtractor objectExtractor = new ObjectExtractor(pdfDocument);
            NurminenDetectionAlgorithm detectionAlgorithm = new NurminenDetectionAlgorithm();
            Map<Integer, List<Rectangle>> detectedTables = new HashMap<>();

            // 4. 获取每页的PageIterator
            PageIterator pages = objectExtractor.extract();

            // 5. 解析每页的Rectangle(table的位置)
            while (pages.hasNext()) {
                Page page = pages.next();
                List<Rectangle> tablesOnPage = detectionAlgorithm.detect(page);
                if (tablesOnPage.size() > 0) {
                    detectedTables.put(Integer.valueOf(page.getPageNumber()), tablesOnPage);
                }
            }

            // 6.通过table位置获取表格具体内容
            SpreadsheetExtractionAlgorithm bea = new SpreadsheetExtractionAlgorithm();

            // 如果单页pdf中有多个表格，则解析出来的List<Rectangle>会有多个对象
            for (Map.Entry<Integer, List<Rectangle>> entry : detectedTables.entrySet()) {
                Page page = objectExtractor.extract(entry.getKey());

                Rectangle rectangle = entry.getValue().get(0);
                Page area = page.getArea(rectangle.getTop(), rectangle.getLeft(), rectangle.getBottom() + 10, rectangle.getRight());

                List<Table> table = bea.extract(area);

                Table t = table.get(0);

                for (int i = 0; i < t.getRowCount(); i++) {

                    if (StringUtils.isBlank(t.getCell(i, 0).getText(false)) || StringUtils.equals(t.getCell(i, 0).getText(false), "序号")) {
                        continue;
                    }

                    CCBTran ccbTran = new CCBTran();
                    ccbTran.setId(t.getCell(i, 0).getText(false));
                    ccbTran.setTransactionType(t.getCell(i, 1).getText(false));
                    ccbTran.setCurrency(currency);
                    ccbTran.setCashRemit(cashRemit);
                    ccbTran.setTransactionDate(t.getCell(i, 2).getText(false));
                    ccbTran.setTransactionAmount(t.getCell(i, 3).getText(false));
                    ccbTran.setBalance(t.getCell(i, 4).getText(false));
                    ccbTran.setTransactionComment(t.getCell(i, 5).getText(false));
                    ccbTran.setCounterParty(t.getCell(i, 6).getText(false));
                    ccbTrans.add(ccbTran);

                }

            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        return ccbTrans;
    }

    public List<List<String>> parseTransTextToTranFieldsList2(String transText) {
        // 这个List每个元素代表一条记录的文本字符串
        List<String> tranTextList = Splitter.on(System.getProperty("line.separator", "\n")).splitToList(transText);
        List<List<String>> tranFieldsList = new ArrayList<List<String>>();

        for (int i = 0; i < tranTextList.size() - 1; i++) {
            // 每一个wechatTranFieldList代表一条记录的所有字段，一个List一条记录
            List<String> wechatTranFields = new ArrayList<>();
            wechatTranFields.add(tranTextList.get(i));
            Collections.addAll(tranFieldsList, wechatTranFields);
        }

        return tranFieldsList;
    }

    // 处理最后两列有换行的case
    private List<CCBTran> parseCcbLastTwoColumns(String filePath, List<CCBTran> ccbTrans) {
        String pdfText = getPdfTextByStripper(filePath);
        pdfText = pdfText.replace(System.getProperty("line.separator", "\n"), " ");

        // 此处一行的格式:1 支付机构提现 人民币元 钞 20210426 10.00 10.00 余额宝提现 215500690/支付宝（中国）网络技术有限公司
        Pattern pattern1 = Pattern.compile("\\d{1,5}\\s\\S{1,24}\\s\\S{1,5}\\s\\S{1,3}\\s\\d{8}\\s");
        Matcher matcher1 = pattern1.matcher(pdfText);

        // 每行交易记录的第一个字所在的index
        int tranIndex = 0;
        Map<Integer, Integer> tranTextStartIndexMap = new HashMap<>();

        while (matcher1.find()) {
//			log.info(
//			"Match \"" + matcher1.group() + "\" at positions " + matcher1.start() + "-" + (matcher1.end() - 1));
            tranTextStartIndexMap.put(tranIndex, matcher1.start());
            tranIndex++;
        }

        // 找出每行交易记录的文本
        List<String> tranTextList = new ArrayList<>();
        for (int i = 1; i < tranTextStartIndexMap.size(); i++) {
            String tranText = pdfText.substring(tranTextStartIndexMap.get(i - 1), tranTextStartIndexMap.get(i));
            if (tranText.contains("生成时间:")) {
                tranText = tranText.substring(0, tranText.indexOf("生成时间:")).trim();
            }
            tranTextList.add(tranText.trim());
        }

        String lastTranText = pdfText.substring(tranTextStartIndexMap.get(tranTextStartIndexMap.size() - 1));
        lastTranText = lastTranText.substring(0, lastTranText.indexOf("生成时间:")).trim();
        tranTextList.add(lastTranText);

        for (int i = 0; i < ccbTrans.size(); i++) {
            // 1 转账存入 人民币元 钞 20210930 0.37 0.37 收回贷款31001079480000000003195478本息 310280000156313110000000001/个贷系统平账专户 A户
            String row = tranTextList.get(i);
            Pattern pattern = Pattern.compile("\\d{1,5}\\s\\S{1,24}\\s\\S{1,5}\\s\\S{1,3}\\s\\d{8}\\s(-?([0-9]{1,3}(,[0-9]{3})*)(.[0-9]{1,2}))\\s(-?([0-9]{1,3}(,[0-9]{3})*)(.[0-9]{1,2}))");//NOSONAR
            Matcher matcher = pattern.matcher(row);

            if (matcher.find()) {
                // 收回贷款31001079480000000003195478本息 310280000156313110000000001/个贷系统平账专户 A户
                String lastTwoColumns = row.substring(matcher.end());
                if (StringUtils.isNotBlank(lastTwoColumns) && lastTwoColumns.contains("/")) {
                    Pattern pattern2 = Pattern.compile("\\s\\d{1,30}/\\S*");
                    Matcher matcher2 = pattern2.matcher(lastTwoColumns);
                    if (matcher2.find()) {
                        // System.out.println("Match \"" + matcher2.group() + "\" at positions " + matcher2.start() + "-" + (matcher2.end() - 1));
                        ccbTrans.get(i).setTransactionComment(lastTwoColumns.substring(0, matcher2.start()).trim());
                        String counterParty = lastTwoColumns.substring(matcher2.start()).trim();
                        if (StringUtils.isNotBlank(counterParty) && counterParty.contains(" ")) {
                            counterParty = counterParty.replaceAll(" ", "");
                        }
                        ccbTrans.get(i).setCounterParty(counterParty);
                    }
                }
                // 对方账号与户名为空, 交易地点/附言不为空, 且有换行
                else if (StringUtils.isNotBlank(lastTwoColumns) && lastTwoColumns.length() > 16) {
                    ccbTrans.get(i).setTransactionComment(lastTwoColumns.trim());
                }
            }
        }

        return ccbTrans;
    }

    private String parseTransToText(String filePath, String separator) {
        PDFTableExtractor extractor = (new PDFTableExtractor()).setSource(filePath);
        int pdfPageNumber = getPdfPageNumber(filePath);

        for (int k = 0; k < pdfPageNumber; k++) {
            int[] skipLinesIndexes = new int[CCB_PDF_HEADER_LINE_NUMBER
                    + CCB_PDF_FOOTER_LINE_NUMBER];
            for (int i = 0; i < CCB_PDF_HEADER_LINE_NUMBER; i++) {
                skipLinesIndexes[i] = i;
            }
            for (int j = 0; j < CCB_PDF_FOOTER_LINE_NUMBER; j++) {
                skipLinesIndexes[CCB_PDF_HEADER_LINE_NUMBER + j] = -(j + 1);
            }
            extractor.exceptLine(k, skipLinesIndexes);
        }

        return extractPdfToText(extractor, separator);
    }

    private static String[] concatenateTransactionType(String[] array, int start, int end) {
        List<String> resultList = new ArrayList<>();
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < array.length; i++) {
            if (i < start || i > end) {
                resultList.add(array[i]);
            } else {
                sb.append(array[i]);
            }
        }
        resultList.add(start, sb.toString());
        return resultList.toArray(new String[0]);
    }

    public static void main(String[] args) {
        CCBPdfParser ccbPdfParser = new CCBPdfParser();
        CCB ccb;
        String json;
//        CCB ccb = ccbPdfParser.parseCCBPdf("D:\\data\\files\\CCB\\bh4y4yhx1940347016444162048_a5eef629f6ae96aee635a16a87b89cf6_beehive-ccb_jyls-0.pdf");
//        String json = JsonUtils.convertObjectToJson(ccb);
//        System.out.println(json);

        ccb = ccbPdfParser.parseCCBPdf("D:\\data\\files\\CCB\\bh4y4yhx1940347016444162048_ca41186dcab17b7b245af8b9997a20f8_beehive-ccb_jyls-0.pdf");
        json = JsonUtils.convertObjectToJson(ccb);
        System.out.println(json);


    }

}
